#include "process.h"
#include "current_thread.h"
#include "file/ConstSmallFile.h"
#include "utility/assertion.h"
#include "wrap/dirent.h"
#include "wrap/unistd.h"

#include <algorithm>
#include <array>
#include <memory>
#include <vector>

namespace _ {

static thread_local int openedFiles = 0;
static thread_local std::vector<pid_t> *pids{};

static int fd_filter(const struct dirent *dir) {
    if (::isdigit(Wrap::d_name_(dir)[0]) != 0) {
        openedFiles++;
    }
    return 0;
}

static int task_filter(const struct dirent *dir) {
    if (::isdigit(Wrap::d_name_(dir)[0]) != 0) {
        pids->emplace_back(std::strtol(Wrap::d_name_(dir), nullptr, 0));
    }
    return 0;
}

static int scandir(const char *dirp, int (*filter)(const struct dirent *)) {
    struct dirent **namelist = nullptr;
    const auto result = Wrap::scandir_alphasort_(dirp, &namelist, filter);
    assert(!namelist);
    return result;
}

}

pid_t Process::pid() {
    return Wrap::getpid_();
}

uid_t Process::uid() {
    return Wrap::getuid_();
}

uid_t Process::euid() {
    return Wrap::geteuid_();
}

long Process::clock_tick_per_second() {
    return Wrap::sysconf_(Wrap::sc_clk_tck());
}

long Process::page_size() {
    return Wrap::sysconf_(Wrap::sc_page_size());
}

std::string Process::hostname() {
    std::array<char, 1 << 8> buffer{};
    if (Wrap::gethostname_(buffer.data(), buffer.size()) != 0) {
        return "unknown host";
    }
    buffer.back() = '\0';
    return buffer.data();
}

std::string Process::exe_path() {
    std::string result{};
    std::array<char, 1 << 10> buffer{};
    auto n = Wrap::readlink_("/proc/self/exe", buffer.data(), buffer.size());
    if (n > 0) {
        result.assign(buffer.data(), static_cast<std::size_t>(n));
    }
    return result;
}

std::string Process::exe_dir() {
    auto path = Process::exe_path();
    return path.substr(0, path.find_last_not_of("/\\") + 1);
}

int Process::opened_files() {
    _::openedFiles = 0;
    _::scandir("/proc/self/fd", _::fd_filter);
    return _::openedFiles;
}

std::vector<pid_t> Process::threads() {
    std::vector<pid_t> result{};
    _::pids = &result;
    _::scandir("/proc/self/task", _::task_filter);
    _::pids = nullptr;
    std::sort(result.begin(), result.end());
    return result;
}

std::string Process::process_status() {
    return ConstSmallFile{"/proc/self/status"}.read_no_more_than(1 << 16);
}

std::string Process::process_stat() {
    return ConstSmallFile{"/proc/self/stat"}.read_no_more_than(1 << 16);
}
